﻿using System;
using System.Collections.Generic;
using System.Text;
using System.Data;

namespace SqlServerDriver
{
	/*
		Created just to break up the large driver class. This will take care of all the 
		commands that generate code or show or search SQL source
	*/ 
	class SourcecodeCommandProcessor
	{
		internal SourcecodeCommandProcessor(SqlServerDataSource parent)
		{
			this.dataSource = parent;
		}

		internal String ProcessCommand(DbView.Command command)
		{
			// Command should contain a parameter
			String param = DbView.Command.TableParameterName(0);
			String objectName = command[param]; // view ?

			// [Specialise: override display text generation ]
			if (command.Cmd == DbView.Commands.Cmnd.TableSource) // they want the create table command
				return this.ScriptTable(this.dataSource, this.TableFromCommand(command));

			if (command.Name == Cmd.ScriptCompare) 
				return this.ScriptCompareTableData(this.dataSource, this.TableFromCommand(command));

			if (command.Name == Cmd.DependencyTree)
				return this.DependencyTree(command, objectName);
			if (command.Name == Cmd.LastProcessCommand)
				return this.LastSqlFor(objectName);
			
			if (command.Name == Cmd.ScriptAddTableProperty)
			{
				// get table name and property name
				String tableName = command[DbView.Command.TableParameterName(0)]; 
				String propertyName = command["PROP"]; 
				return ScriptExtendexProperties(tableName, propertyName, "USER_TABLE", ExtendedPropertyScripting.ScriptOption.addTable);
			}
			if (command.Name == Cmd.ScriptAddAllTableProperties)
			{
				// get property name - leave table blank to indicate all tables wanted
				String propertyName = command["PROP"]; 
				return ScriptExtendexProperties("", propertyName, "USER_TABLE", ExtendedPropertyScripting.ScriptOption.addTable);
			}
			if (command.Name == Cmd.DropTableProperty)
			{
				// get table name and property name
				String tableName = command[DbView.Command.TableParameterName(0)]; 
				String propertyName = command["PROP"]; 
				return ScriptExtendexProperties(tableName, propertyName, "USER_TABLE", ExtendedPropertyScripting.ScriptOption.dropTable);
			}
			if (command.Name == Cmd.ScriptAddTableColumnProperty)
			{
				// get table name and property name
				String tableName = command[DbView.Command.TableParameterName(0)]; 
				String propertyName = command["PROP"]; 
				return ScriptExtendexProperties(tableName, propertyName, "COLUMN", ExtendedPropertyScripting.ScriptOption.addColumn);
			}
			if (command.Name == Cmd.ScriptAddAllColumnProperties)
			{
				// get property name - leave table blank to indicate all tables wanted
				String propertyName = command["PROP"]; 
				return ScriptExtendexProperties("", propertyName, "COLUMN", ExtendedPropertyScripting.ScriptOption.addColumn);
			}
			if (command.Name == Cmd.DropColumnProperty)
			{
				// get table name and property name
				String tableName = command[DbView.Command.TableParameterName(0)]; 
				String propertyName = command["PROP"]; 
				return ScriptExtendexProperties(tableName, propertyName, "COLUMN", ExtendedPropertyScripting.ScriptOption.dropColumn);
			}

			if (command.Cmd == DbView.Commands.Cmnd.ShowSource)  // generic constion
			{
				// assume we get a hint from the parameters
				if (command["TYPE"] == "DATA")
				{
					String tableName = command["TABLE"];
					// overwrite the official place for the object
					command.AddParam(DbView.Command.TableParameterName(0), tableName);
					return this.ScriptData(this.dataSource, tableName);
				}
				if (command["TYPE"] == "FK")
				{
					return this.ScriptForeignKeys(this.dataSource, objectName, false, false);
				}
				if (command["TYPE"] == "FKALL")
				{
					return this.ScriptForeignKeys(this.dataSource, objectName, true, false);
				}
				if (command["TYPE"] == "XFK")
				{
					return this.ScriptForeignKeys(this.dataSource, objectName, false, true);
				}
				if (command["TYPE"] == "XFKALL")
				{
					return this.ScriptForeignKeys(this.dataSource, objectName, true, true);
				}
			}
			else if (SqlServerBrokerPopupBuilder.IsBrokerCommand(command))
				return BrokerScripting.MakeBrokerScript(command, this.dataSource.ConnectionString);

			// default / original look in syscomments
			String sourceCode = Helper.GetSourceCode(this.dataSource.ConnectionString, objectName);
			if (sourceCode.Length == 0)
				return this.MakeNoSourceCodeMessage(objectName);
			return sourceCode;
		}

		private string TableFromCommand(DbView.Command command)
		{
			if (command["TABLE"] != null)
				return command["TABLE"];
			return command[DbView.Command.TableParameterName(0)];
		}

		private string MakeNoSourceCodeMessage(string objectName)
		{
			String sql = SqlServerTemplates.SourceCodeAccessPermissionSql();
			String res = Helper.GenericScalarQuery(this.dataSource.ConnectionString, sql, "");
			if (res == "0")
				return String.Format(NO_PERMISSION_TO_SOURCE_CODE, objectName);
			else
				return String.Format(NO_SOURCE_CODE, objectName);
		}

		// Queries database for source of Views, Stored procedures, Triggers etc.
		internal String GetSourceCode(DbView.ViewContext context, String objectName)
		{
			//String objectName = context.ViewDef.TableName;
			String sourceCode = Helper.GetSourceCode(this.dataSource.ConnectionString, objectName);
			if (sourceCode.Length == 0)
			    return this.MakeNoSourceCodeMessage(objectName);
			return sourceCode;
		}
		// Searches the source of Views, Stored procedures, Triggers etc. for the search text held in the context
		internal string SearchSourceCommand(DbView.ViewContext context)
		{
		    String searchText = context.Command["SEARCH"]; // what to search ffor
		    String filter = context.FilterString;          // what to filter object names on (rarely used?)
		    String template = SqlServerTemplates.Templates.FetchTemplate(this.dataSource.SqlVersion, context.Command.Cmd);
		    // replace search string using the 
		    template = SqlServerTemplates.Templates.ProcessFilterClause(template, "{", searchText, this.dataSource.IsCaseSensitive);
		    template = SqlServerTemplates.Templates.ProcessFilterClause(template, filter, this.dataSource.IsCaseSensitive);
		    String commandString = String.Format(template, searchText.Replace("'", "''")
																	 .Replace("_","[_]")); // A bit hacky considering there is a mechanism for handling text search
		    return commandString;
		}

		/////////////////////////////////////////////////////////////////////////
		// helpers
		private String ScriptTable(DbViewDriver.IDataSource source, String tableName)
		{
			SqlTableSchema schema = this.dataSource.MakeSchemaFor(tableName);
			SqlServerScripter scripter = new SqlServerScripter(tableName, schema);
			return scripter.ScriptCreateTable();
		}

		private String ScriptForeignKeys(DbViewDriver.IDataSource source, String tableName, bool allTables, bool dropOnly)
		{
			String commandTemplate = SqlServerTemplates.Templates.FetchTemplate(this.dataSource.SqlVersion, DbView.Commands.Cmnd.ForeignKeys);
			String sql = TableCommandProcessor.AddFilterClause(commandTemplate, "", this.dataSource.IsCaseSensitive);
			DataSet dataset = new DataSet();
			Helper.GenericMakeDset(this.dataSource.ConnectionString, sql, tableName, out dataset);
			FkScripter scripter = new FkScripter();
			scripter.ConfigureTable( dataset.Tables[0], 0, 4, 1, 2, 3); // Sorry
			scripter.DisabledCol = 5;
			scripter.DeleteActionCol = 7;
			scripter.UpdateActionCol = 8;
			scripter.ReplicationCol = 9;
			String script = scripter.ScriptKeys(tableName, allTables, dropOnly);
			return script;
		}

		private String ScriptData(DbViewDriver.IDataSource source, String tableName)
		{
			SqlTableSchema schema = this.dataSource.MakeSchemaFor(tableName);
			SqlServerScripter scripter = new SqlServerScripter(tableName, schema);
			DataSet dataset = new DataSet();
			Helper.GenericMakeDset(this.dataSource.ConnectionString, "select * from "+tableName, tableName, out dataset);
			DataTable dataTable = dataset.Tables[0];
			
			return scripter.ScriptData(dataTable);
		}
		
		// itemType is table/column as reported in the summary of extended properties,
		// scriptOption is the action. 
		private string ScriptExtendexProperties(String tableName, String propertyName, String itemType, ExtendedPropertyScripting.ScriptOption scriptOption)
		{
			String script = ExtendedPropertiesTemplates.GetExtendedPropertiesTemplate(itemType, propertyName);
			// process add the table search
			script = SqlServerTemplates.Templates.ProcessFilterClause(script, tableName, false);
			// get a dataset
			DataTable dt2 = Helper.GenericQuery(this.dataSource.ConnectionString, script);
			// iterate the data set and build the string.
			StringBuilder sb = new StringBuilder();
			foreach (DataRow dr in dt2.Rows)
			{
				ExtendedPropertyScripting.ScriptPropertyInfoLine(sb, dr, scriptOption);
			}
			return ExtendedPropertyScripting.MakeScript(sb, scriptOption);
		}
		//private String GenerateTableClass(DbViewDriver.IDataSource source, String tableName)
		//{
		//    SqlTableSchema schema = this.m_Source.MakeSchemaFor(tableName);
		//    LinqTableGenerator Scripter = new LinqTableGenerator(tableName, schema);
		//    return Scripter.GenerateCode(source.ConnectionString);
		//}
		private String ScriptCompareTableData(DbViewDriver.IDataSource source, String tableName)
		{
			SqlTableSchema schema = this.dataSource.MakeSchemaFor(tableName);
			SqlServerScripter scripter = new SqlServerScripter(tableName, schema);
			return scripter.ScriptCompareTableData();
		}

		// Suspect this is unused
		private String ScriptSynchContents(DbViewDriver.IDataSource source, String tableName, DbView.ConnectionInfo refreshSourceConnInfo)
		{
			SqlTableSchema schema = this.dataSource.MakeSchemaFor(tableName);
			SqlServerScripter scripter = new SqlServerScripter(tableName, schema);
			SqlScriptingHelper scriptHelper = new SqlScriptingHelper(tableName, schema);
			return scripter.ScriptRowsetRefresh(tableName, refreshSourceConnInfo, scriptHelper);
		}

		String DependencyTree(DbView.Command command, String objectName)
		{
			String template = SqlServerTemplates.Templates.FetchExtensionTemplate(this.dataSource.SqlVersion, command, command.Name);
			String sql = String.Format(template, objectName);
			String sourceCode = Helper.DocFromDset(this.dataSource.ConnectionString, sql, 0, true);
			sourceCode = String.Format("{0}\n{1}", objectName, sourceCode);
			return sourceCode;
		}

		// tries to fish out the last SQL executed by a process. 
		private string LastSqlFor(string processId)
		{
			String sql = String.Format("DBCC inputbuffer({0})", processId);
			DataSet dset;
			Helper.GenericMakeDset(this.dataSource.ConnectionString, sql, "SQL", out dset);
			String sourceCode = "Cannot find the information";
			// Checking that whats returned is expected.
			if (dset.Tables.Count > 0)
				if (dset.Tables[0].Rows.Count > 0)
					if (dset.Tables[0].Columns.Count > 2)
						sourceCode = dset.Tables[0].Rows[0][2].ToString();
			return sourceCode;
		}
		SqlServerDataSource dataSource;

		const String NO_SOURCE_CODE = @"
/*
	Could not find source for {0}.

	This is either because the object does not exist in the database or the connection
	you are using does not have permission to access the source.
*/
";
		const String NO_PERMISSION_TO_SOURCE_CODE = @"
/*
	Could not access source for {0}.

	A quick check indicates that the current connection does not have
	VIEW DEFINITION permission, which the programm requires for any operations
	that involve examining or reporting view, stored procedure, function etc 
	source code. 
*/
";
	}
}
